/*
 * Copyright (c) 2018 Zhang Hai <Dreaming.in.Code.ZH@Gmail.com>
 * All Rights Reserved.
 */

package me.zhanghai.openharmony.materialplaypausedrawable;


import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorProperty;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.Image;
import ohos.agp.render.*;
import ohos.agp.utils.Color;
import ohos.agp.utils.Matrix;
import ohos.app.Context;

public class MaterialPlayPauseImage extends Image implements Component.DrawTask {

    private Paint mPaint;
    private final Path mPath = new Path();
    private final Matrix mMatrix = new Matrix();
    private float mFraction = 1;
    private AnimatorProperty animatorProperty;
    private State mPreviousState;
    private State mCurrentState = State.Play;
    private State mNextState;
    private AnimatorValue animatorValue;
    private ColorFilter mTintFilter;
    private ColorFilter mColorFilter;

    public enum State {

        Play(new int[]{
                8, 5, 8, 12, 19, 12, 19, 12,
                8, 12, 8, 19, 19, 12, 19, 12
        }, new int[]{
                12, 5, 5, 16, 12, 16, 12, 5,
                12, 5, 12, 16, 19, 16, 12, 5
        }),
        Pause(new int[]{
                6, 5, 6, 19, 10, 19, 10, 5,
                14, 5, 14, 19, 18, 19, 18, 5
        }, new int[]{
                5, 6, 5, 10, 19, 10, 19, 6,
                5, 14, 5, 18, 19, 18, 19, 14
        });

        private final int[] mStartPoints;
        private final int[] mEndPoints;

        State(int[] startPoints, int[] endPoints) {
            mStartPoints = startPoints;
            mEndPoints = endPoints;
        }

        public int[] getStartPoints() {
            return mStartPoints.clone();
        }

        public int[] getEndPoints() {
            return mEndPoints.clone();
        }
    }

    public MaterialPlayPauseImage(Context context) {
        super(context);
        init();
    }

    public MaterialPlayPauseImage(Context context, AttrSet attrs) {
        super(context, attrs);
        init();
    }

    public MaterialPlayPauseImage(Context context, AttrSet attrs, String defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        setScaleMode(ScaleMode.CLIP_CENTER);
        animatorValue = new AnimatorValue();
        animatorValue.setDuration(ViewUtils.getShortAnimTime(getContext()));
        animatorValue.setValueUpdateListener(new AnimatorValue.ValueUpdateListener() {
            @Override
            public void onUpdate(AnimatorValue animatorValue, float value) {
                mFraction = value;
                invalidate();
            }
        });
        animatorProperty = createAnimatorProperty();
        animatorProperty.setDuration(ViewUtils.getShortAnimTime(getContext()));
        animatorProperty.setCurveType(Animator.CurveType.BOUNCE);
        initPaint();
        mTintFilter = new ColorFilter(Color.WHITE.getValue(), BlendMode.SRC_IN);
        addDrawTask(this);
    }

    private void initPaint() {
        if (mPaint == null) {
            mPaint = new Paint();
            mPaint.setAntiAlias(true);
            mPaint.setColor(Color.BLACK);
            mPaint.setStyle(Paint.Style.FILL_STYLE);
        }

        mPaint.setAlpha(0xFF);
        mPaint.setColorFilter(getColorFilterForDrawing());
    }

    public void setAnimationDuration(long duration) {
        animatorProperty.setDuration(duration);
        animatorValue.setDuration(duration);
    }

    public State getState() {
        return getPlayPauseState();
    }

    protected ColorFilter getColorFilterForDrawing() {
        return mColorFilter != null ? mColorFilter : mTintFilter;
    }

    public void setColor(Color color) {
        setColorFilter(new ColorFilter(color.getValue(), BlendMode.SRC_IN));
    }

    public void setColorFilter(@Nullable ColorFilter colorFilter) {
        mColorFilter = colorFilter;
        invalidate();
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        initPaint();
        mMatrix.setScale((float) getEstimatedWidth() / 24, (float) getEstimatedHeight() / 24);
        if (mFraction == 0) {
            drawState(canvas, mPaint, mPreviousState);
        } else if (mFraction == 1) {
            drawState(canvas, mPaint, mCurrentState);
        } else {
            drawBetweenStates(canvas, mPaint, mPreviousState, mCurrentState, mFraction);
        }
    }

    private void drawState(Canvas canvas, Paint paint, State state) {
        int[] points = state.getStartPoints();
        mPath.rewind();
        for (int i = 0, count = points.length, subCount = count / 2; i < count; i += 2) {
            float x = points[i];
            float y = points[i + 1];
            if (i % subCount == 0) {
                if (i > 0) {
                    mPath.close();
                }
                mPath.moveTo(x, y);
            } else {
                mPath.lineTo(x, y);
            }
        }
        mPath.close();
        mPath.transform(mMatrix);
        canvas.drawPath(mPath, paint);
    }

    private void drawBetweenStates(Canvas canvas, Paint paint, State fromState, State toState,
                                   float fraction) {
        mMatrix.preRotate(MathUtils.lerp(0, 90, fraction), 12, 12);
        int[] startPoints = fromState.getStartPoints();
        int[] endPoints = toState.getEndPoints();
        mPath.rewind();
        for (int i = 0, count = startPoints.length, subCount = count / 2; i < count; i += 2) {
            int startX = startPoints[i];
            int startY = startPoints[i + 1];
            int endX = endPoints[i];
            int endY = endPoints[i + 1];
            float x = MathUtils.lerp(startX, endX, fraction);
            float y = MathUtils.lerp(startY, endY, fraction);
            if (i % subCount == 0) {
                if (i > 0) {
                    mPath.close();
                }
                mPath.moveTo(x, y);
            } else {
                mPath.lineTo(x, y);
            }
        }
        mPath.close();
        mPath.transform(mMatrix);
        canvas.drawPath(mPath, paint);
    }

    public void jumpToState(State state) {
        stop();
        mPreviousState = null;
        mCurrentState = state;
        mFraction = 1;
        mNextState = null;
        invalidate();
    }

    public void setState(State state) {
        if (animatorValue.isRunning() || animatorValue.isRunning()) {
            return;
        }

        if (mCurrentState == state) {
            mNextState = null;
            return;
        }
        if (getVisibility() != VISIBLE) {
            jumpToState(state);
            return;
        }
        mNextState = state;
        tryMoveToNextState();
    }

    private void tryMoveToNextState() {
        if (mNextState == null || isRunning()) {
            return;
        }
        mPreviousState = mCurrentState;
        mCurrentState = mNextState;
        mFraction = 0;
        mNextState = null;
        start();
    }

    private boolean isRunning() {
        return animatorProperty.isRunning() || animatorValue.isRunning();
    }

    public State getPlayPauseState() {
        return mNextState != null ? mNextState : mCurrentState;
    }

    private void start() {
        if (animatorProperty.isRunning() || animatorValue.isRunning()) {
            return;
        }
        animatorProperty.start();
        animatorValue.start();
        invalidate();
    }

    private void stop() {
        if (!animatorProperty.isRunning() && !animatorValue.isRunning()) {
            return;
        }
        animatorProperty.end();
        animatorValue.end();
    }
}
